package com.zazworka.jira.rest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Pattern;

import com.atlassian.plugins.rest.common.security.AnonymousAllowed;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;


/**
 * A resource of message.
 */
@Path("/svncommentqa")
public class SVNCommentsRestResource {

	
	
    @SuppressWarnings("deprecation")
	@GET
    @AnonymousAllowed
    @Path("svncommentqa")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getMessage(
    		@QueryParam ("svnURL") String svnURL, 
    		@QueryParam ("svnPath") String svnPath, 
    		@QueryParam ("svnUser") String svnUser,
    		@QueryParam ("svnPassword") String svnPassword,
    		@QueryParam ("numberOfRevisions") String numberOfRevisions
    	)
    {
    	
    	try{
    		//number of comment that have an empty message
    		Long commentEmpty = 0L;
    		//number of comments that have message, but not JIRA link
    		Long commentText = 0L;
    		//number of comments that have JIRA link
    		Long commentJIRALink = 0L;
    		
    		Long numberOfRevisionsLong = Long.parseLong(numberOfRevisions);
        	SVNRepository repository = connectToSVN(svnURL, svnPath, svnUser, svnPassword);
        	
        	Long latestRevision = getLatestRevisionNumber(svnURL, svnPath, svnUser, svnPassword);
        	
        	Long firstRevision = Math.max(0L, latestRevision-numberOfRevisionsLong);
        	
        	SVNQAReport report = new SVNQAReport(null,new ArrayList<SVNCommentsRestResource.TimeReport>());
        	
        	//Pattern p = Pattern.compile(".*([A-Z])+-([0-9])+.*");
        	
        	
        	while(latestRevision>=0L){
        		Collection<SVNLogEntry> svnLogEntries = null;
				
        		try{
        			svnLogEntries = repository.log(new String[] {  },
        					null, latestRevision,firstRevision, false, false);
        		}
        		catch(SVNException e){
        			//occurs if path cannot be found before a certain revision, e.g. it has not yet been created
        			break;
        		}
        		
        		
				for(SVNLogEntry svnLogEntry : svnLogEntries){
					
					String comment = svnLogEntry.getMessage();
					
					Date date = svnLogEntry.getDate();
					
					//if date is older than 1 year ago break out of both loops
					if(date.before(new Date(date.getYear()-1,date.getMonth(),0))){
						latestRevision=-1L; //forces to break out of outer loop
						break;
					}
					
					String dateLabel = ""+(date.getMonth()+1)+"/"+(date.getYear()+1900);
					
					//lookup time report
					TimeReport timeReport = null;
					for(TimeReport tr : report.getTimeReports()){
						if(tr.getLabel().equals(dateLabel)){
							timeReport=tr;
							break;
						}
					}
					
					if(timeReport == null ){
						timeReport = new TimeReport(dateLabel,0L,0L,0L);
						report.getTimeReports().add(timeReport);
					}
					
					if(comment==null || comment.trim().equals("")){
						timeReport.setNumberCommentEmpty(timeReport.getNumberCommentEmpty()+1);
					}
					else{
						//search for JIRA style project reference (might produce false positives, since not checked if project and issue id exists)
						if(comment.matches("(?s).*([A-Z])+-([0-9])+.*")){
							timeReport.setNumberCommentJIRALinked(timeReport.getNumberCommentJIRALinked()+1);
						}
						else{
							timeReport.setNumberCommentText(timeReport.getNumberCommentText()+1);
						}
					}
				}
				
				//set new first and last revision
				firstRevision=Math.max(0L, firstRevision-numberOfRevisionsLong);
				latestRevision=latestRevision-numberOfRevisionsLong;
        	}
			
			return Response.ok(report).build();
    	}
    	catch(Exception e) {
    		return Response.ok(new SVNQAReport(e.getMessage()+"\n"+Arrays.toString(e.getStackTrace()), null)).build(); 
    	}
    	
       
    }
    
    private SVNRepository connectToSVN(String svnURL, String svnPath, String svnUser, String svnPassword) throws SVNException{
    	
    	SVNRepository repository = null;
    	
    	DAVRepositoryFactory.setup();

		System.out.println(svnURL);
		repository = SVNRepositoryFactory.create(SVNURL
				.parseURIEncoded(svnURL+"/"+svnPath));
		// create authentification object
		ISVNAuthenticationManager authManager = SVNWCUtil
				.createDefaultAuthenticationManager(svnUser,svnPassword);
		repository.setAuthenticationManager(authManager);
		
		return repository;
    }
    
    private Long getLatestRevisionNumber(String svnURL, String svnPath, String svnUser, String svnPassword) throws SVNException{
    	// iterate log entries, store touched elements as entity revisions
		Long latestRevision=null;
		//get latest revision id:
		//this requires to create a second repository object that gets the latest revision of the given repository URL + path.
		//simply latestRevision = repository.getLatestRevision() will not work if access is restricted to a specific path in the repository.
		SVNRepository repositoryTemp = SVNRepositoryFactory.create(SVNURL
				.parseURIEncoded(svnURL+"/"+svnPath));
		// create authentification object
		ISVNAuthenticationManager authManager = SVNWCUtil
				.createDefaultAuthenticationManager(svnUser,
						svnPassword);
		repositoryTemp.setAuthenticationManager(authManager);
		latestRevision = repositoryTemp.getLatestRevision();
		return latestRevision;
		
    }
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class SVNQAReport {

        @XmlElement
        private String errorMessage;
        
        @XmlElement
        private ArrayList<TimeReport> timeReports;

        public SVNQAReport(String errorMessage, ArrayList<TimeReport> timeReports) {
        	this.errorMessage=errorMessage;
        	this.timeReports=timeReports;
        }
        
        private SVNQAReport(){}

        public String getErrorMessage(){
        	return this.errorMessage;
        }
        
        public ArrayList<TimeReport> getTimeReports(){
        	return this.timeReports;
        }
        
    }
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class TimeReport{
    	
    	@XmlElement
    	private String label;
    	
    	@XmlElement
    	private Long numberCommentEmpty;
    	
    	@XmlElement
    	private Long numberCommentText;
    	
    	@XmlElement
    	private Long numberCommentJIRALinked;
    	
    	public TimeReport(String label, Long numberCommentEmpty, Long numberCommentText, Long numberCommentJIRALinked){
    		this.label=label;
    		this.numberCommentEmpty=numberCommentEmpty;
    		this.numberCommentText=numberCommentText;
    		this.numberCommentJIRALinked=numberCommentJIRALinked;
    	}
    	
    	private TimeReport(){}
    	
    	public String getLabel(){
    		return label;
    	}
    	
    	public Long getNumberCommentEmpty(){
    		return numberCommentEmpty;
    	}
    	
    	public Long getNumberCommentText(){
    		return numberCommentText;
    	}
    	
    	public Long getNumberCommentJIRALinked(){
    		return numberCommentJIRALinked;
    	}
    	
    	public void setNumberCommentEmpty(Long numberCommentEmpty) {
			this.numberCommentEmpty = numberCommentEmpty;
		}

		public void setNumberCommentText(Long numberCommentText) {
			this.numberCommentText = numberCommentText;
		}

		public void setNumberCommentJIRALinked(Long numberCommentJIRALinked) {
			this.numberCommentJIRALinked = numberCommentJIRALinked;
		}
    	
    	
    	
    }
}